package main

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/golang/protobuf/proto"
	"github.com/jinzhu/gorm"
	"github.com/yosssi/gmq/mqtt"
	"github.com/yosssi/gmq/mqtt/client"
	"log"
	"net/http"
	"os"
	"os/signal"
	"recharge/guanbo"
	"recharge/logger"
	"sync"
	"time"
)

var cli *client.Client
var srv http.Server
var _db *gorm.DB

const (
	EXECUTE_STATUS_NOTIFY  = 0 //下发通知
	EXECUTE_STATUS_SUCCESS = 1 //下发通知成功
	EXECUTE_STATUS_ERROR   = 2 //下发通知失败

	ACTION_CLOSE = 1 //关闭电源
	ACTION_OPEN  = 2 //打开电源

	MSG_TYPE_QUERY   = 1 //查询
	MSG_TYPE_CONTROL = 2 //控制

	DEVICE_CALL_BACK_URL = "https://www.xdkjiot.com/v1/api/end"
)

func main() {
	// Set up channel on which to send signal notifications.
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Interrupt, os.Kill)
	// Create an MQTT Client.
	cli = client.New(&client.Options{
		// Define the processing of the error handler.
		ErrorHandler: func(err error) {
			logger.Log.Error(err)
			return
		},
	})
	// Terminate the Client.
	defer cli.Terminate()
	// Connect to the MQTT Server.
	err := cli.Connect(&client.ConnectOptions{
		Network:  "tcp",
		Address:  "47.116.4.43:1883",
		ClientID: []byte("充电后台"),
		UserName: []byte("admin"),
		Password: []byte("publish"),
	})
	if err != nil {
		logger.Log.Error(err)
		return
	}
	//初始化设备
	// SubscribeAllDeviceClients()
	wg := &sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		HttpServer()
		logger.Log.Debug("http server 结束")
	}()
	wg.Add(1)
	ctx, cancel := context.WithCancel(context.Background())
	go CronListenDeviceList(wg, ctx)
	go CronEndOrder(wg, ctx)
	wg.Add(1)
	go func() {
		defer wg.Done()
		<-sigc
		cancel()
		_ = _db.Close()
		logger.Log.Debug("Ctrl+C Exit")
		// Disconnect the Network Connection.
		if err := cli.Disconnect(); err != nil {
			logger.Log.Error(err)
		}
		if err := srv.Shutdown(ctx); err != nil {
			log.Fatalf("Server Shutdown Failed:%+v", err)
		}
	}()
	wg.Wait()
}

func SubscribeAllDeviceClients() {
	db := GetDB()
	//查询设备更新状态在3分钟前的
	var queryResult []*Device
	db.Table(new(Device).TableName()).Find(&queryResult)
	for _, q := range queryResult {
		topic := fmt.Sprintf("$SYS/brokers/emqx@127.0.0.1/clients/%s/connected", q.EquipmentNum)
		err := cli.Subscribe(&client.SubscribeOptions{
			SubReqs: []*client.SubReq{
				&client.SubReq{
					TopicFilter: []byte(topic),
					QoS:         mqtt.QoS0,
					// 接收设备消息
					Handler: func(topicName, message []byte) {
						logger.Log.Debug("[设备基础信息回传信息]", string(topicName), string(message))
						var c guanbo.ClientInfo
						err := proto.Unmarshal(message, &c)
						if err != nil {
							logger.Log.Error(err)
							return
						}
						logger.Log.Info("[设备连接成功]", q.EquipmentNum)
						db.Table(new(Device).TableName()).Where(&Device{EquipmentNum: q.EquipmentNum}).Update(&Device{UpdatedAt: time.Now()})
						time.Sleep(3 * time.Second)
						err = GBRspClientInfo(q.EquipmentNum, defaultRatedPower, defaultMaxRechargeTime,
							defaultPeriodTime, defaultContinueTime, defaultContinuePower,
							defaultEndTime, defaultMinPower)
						if err != nil {
							logger.Log.Error(err)
						}
						time.Sleep(5 * time.Second)
						//获取插座
						err = GBGetDeviceSocketList(q.EquipmentNum, 0, 0, ACTION_OPEN, MSG_TYPE_CONTROL)
						if err != nil {
							logger.Log.Error(err)
						}
					},
				},
			},
		})
		if err != nil {
			logger.Log.Fatal(err)
			return
		}
	}
}

func FormatTime(ts int64) string {
	t := time.Unix(ts, 0)
	return t.Format("2006-01-02 15:04:05")
}

const (
	defaultRatedPower      = 1000
	defaultMaxRechargeTime = 720
	defaultPeriodTime      = 3
	defaultContinueTime    = 3
	defaultContinuePower   = 30
	defaultEndTime         = 1
	defaultMinPower        = 25
)

func CheckDevice(gap int64) {
	var err error
	db := GetDB()
	//查询设备更新状态在3分钟前的
	var queryResult []*Device
	db.Table(new(Device).TableName()).Where("updated_at < ?",
		FormatTime(time.Now().Unix()-gap)).Find(&queryResult)
	if len(queryResult) > 0 {
		for _, m := range queryResult {
			//先订阅设备
			if gap <= 0 { //刚启动server 监听订阅
				err = BGSubscribeDeviceByID(m.EquipmentNum)
				if err != nil {
					logger.Log.Error(err)
				}
				time.Sleep(3 * time.Second)
			}
			//最后更新时间
			logger.Log.Info(m.EquipmentNum, "[设备最后更新时间]", m.UpdatedAt, time.Now().Unix()-m.UpdatedAt.Unix())
			if gap <= 0 || time.Now().Unix()-m.UpdatedAt.Unix() > 1200 {
				//如果10分钟未更新说明 离线了
				//设置参数 激活设备
				err = GBRspClientInfo(m.EquipmentNum, defaultRatedPower, defaultMaxRechargeTime,
					defaultPeriodTime, defaultContinueTime,
					defaultContinuePower, defaultEndTime, defaultMinPower)
				if err != nil {
					logger.Log.Error(err)
				}
				time.Sleep(10 * time.Second)
			}
			//获取插座 ping
			// err = GBGetDeviceSocketList(m.EquipmentNum, 0, 0, ACTION_OPEN, MSG_TYPE_CONTROL)
			// if err != nil {
			// 	logger.Log.Error(err)
			// }
		}
	}
}

func CronListenDeviceList(wg *sync.WaitGroup, ctx context.Context) {
	//设备监听启动
	CheckDevice(0)
	logger.Log.Info("设备监听启动")
	defer wg.Done()
	t := time.NewTicker(30 * time.Minute)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			logger.Log.Info("设备监听 停止")
			return
		case <-t.C:
			CheckDevice(180)
		}
	}
}

//给设备发送消息
func SendMsgToDevice(did string, channelID, maxTime, action, msgType int) error {
	c := &DeviceCommand{}   //设备指令
	c.ExecuteStatus = 0     // 执行命令 1 success 2 error
	c.ChannelID = channelID // 执行命令
	c.MaxTime = maxTime     // 执行命令
	c.MsgSN = 12            // 消息编码
	c.MsgType = msgType     // 消息编码 1 查询 2 控制
	c.Action = action       // 是2打开 1关闭
	b, _ := json.Marshal(c)
	logger.Log.Info("[给指定设备传输通讯]", string(b))
	h := hex.EncodeToString(b) //传给机器 16进制
	// Publish a message.
	err := cli.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS0,
		TopicName: []byte(fmt.Sprintf("/service/%s", did)),
		Message:   []byte(h),
	})
	return err
}

func GetDB() *gorm.DB {
	var err error
	if _db == nil || _db.Error != nil {
		_db, err = gorm.Open("mysql", "root:1227Jiang@tcp(101.133.217.234:3306)/xidian?charset=utf8&parseTime=True&loc=Asia%2FShanghai")
		if err != nil {
			logger.Log.Debug("数据库连接失败", err)
			return nil
		}
		logger.Log.Info("数据库连接成功")
		_db.AutoMigrate(&JiaMeng{})
	}
	return _db
}

//http 服务
func HttpServer() {
	http.HandleFunc("/jiameng", HandleJiameng)
	http.HandleFunc("/mobile/send", HandleMobileSend)
	http.HandleFunc("/device/set", SetDevice)
	http.HandleFunc("/device/get-sockets", GetDeviceSockets)
	http.HandleFunc("/device/open", OpenDevice)
	http.HandleFunc("/device/close", CloseDevice)
	http.HandleFunc("/device/subscribe", SubscribeDevice)
	http.HandleFunc("/device/unsubscribe", UnSubscribeDevice)
	http.HandleFunc("/device/set-number", SetDeviceNumber)
	srv.Addr = ":8088"
	logger.Log.Debug("http server 启动 :8088")
	if err := srv.ListenAndServe(); err != http.ErrServerClosed {
		// Error starting or closing listener:
		logger.Log.Errorf("HTTP server ListenAndServe: %v", err)
	}
}
